#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
//#include <asm/mach/map.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>

 #define LED_MAJOR 200 /* 主设备号 */
 #define LED_NAME "led" /* 设备名字 */

 #define LEDOFF 0 /* 关灯 */
 #define LEDON 1 /* 开灯 */

 #define PMU_GRF_BASE (0xFD5F8000)
 #define BUS_IOC_BASE (0xFD5F8000)
 #define BUS_IOC_GPIO1A_IOMUX_SEL_L (BUS_IOC_BASE + 0x0020)
 #define VCCIO1_4_BASE (0xFD5F9000)
 #define VCCIO1_4_IOC_GPIO1A_DS_L (VCCIO1_4_BASE + 0x0020)

 #define GPIO1_BASE (0xFEC20000)
 #define GPIO_SWPORT_DR_L (GPIO1_BASE + 0X0000)
 #define GPIO_SWPORT_DDR_L (GPIO1_BASE + 0X0008)
 /* 映射后的寄存器虚拟地址指针 */
static void __iomem *BUS_IOC_GPIO1A_IOMUX_SEL_L_PI;
static void __iomem *VCCIO1_4_IOC_GPIO1A_DS_L_PI;
static void __iomem *GPIO_SWPORT_DR_L_PI;
static void __iomem *GPIO_SWPORT_DDR_L_PI;


/*
* @description : LED 打开/关闭
* @param - sta : LEDON(0) 打开 LED，LEDOFF(1) 关闭 LED
* @return : 无
*/
void led_switch(u8 sta)
{
    u32 val = 0;
    if(sta == LEDON) {
        val = readl(GPIO_SWPORT_DR_L_PI);
        val &= ~(0X8 << 0); /* bit3 清零*/
        val |= ((0X8 << 16) | (0X8 << 0)); /* bit19 置 1，允许写 bit3，bit3，高电平*/
        writel(val, GPIO_SWPORT_DR_L_PI);
    }else if(sta == LEDOFF) {
        writel(val, GPIO_SWPORT_DR_L_PI);
        val = readl(GPIO_SWPORT_DR_L_PI);
        val &= ~(0X8 << 0); /* bit3 清零*/
        val |= (0X8 << 16); /* bit19 置 1，允许写 bit3，
        bit3，低电平 */
        writel(val, GPIO_SWPORT_DR_L_PI);
    } 
}

/*
 * @description : 物理地址映射
 * @return : 无
*/
void led_remap(void)
{
    BUS_IOC_GPIO1A_IOMUX_SEL_L_PI = ioremap(BUS_IOC_GPIO1A_IOMUX_SEL_L, 4);
    VCCIO1_4_IOC_GPIO1A_DS_L_PI = ioremap(VCCIO1_4_IOC_GPIO1A_DS_L, 4);
    GPIO_SWPORT_DR_L_PI = ioremap(GPIO_SWPORT_DR_L, 4);
    GPIO_SWPORT_DDR_L_PI = ioremap(GPIO_SWPORT_DDR_L, 4);
}

/*
* @description : 取消映射
* @return : 无
*/
void led_unmap(void)
{
    /* 取消映射 */
    iounmap(BUS_IOC_GPIO1A_IOMUX_SEL_L_PI);
    iounmap(VCCIO1_4_IOC_GPIO1A_DS_L_PI);
    iounmap(GPIO_SWPORT_DR_L_PI);
    iounmap(GPIO_SWPORT_DDR_L_PI);
}

/*
* @description : 打开设备
* @param - inode : 传递给驱动的 inode
* @param - filp : 设备文件，file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
    return 0;
}

/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数，如果为负值，表示读取失败
*/
static ssize_t led_read(struct file *filp,
                         char __user *buf,
                         size_t cnt,
                          loff_t *offt)
{
    return 0;
}

/*
* @description : 向设备写数据
* @param - filp : 设备文件，表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数，如果为负值，表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user*buf, size_t cnt, loff_t *offt)
{
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0) {
        printk(KERN_INFO "kernel write failed!\r\n");
        return -EFAULT;
    }

    ledstat = databuf[0]; /* 获取状态值 */

    if(ledstat == LEDON)
    { 
        led_switch(LEDON); /* 打开 LED 灯 */
    }
    else if(ledstat == LEDOFF)
    {
        led_switch(LEDOFF); /* 关闭 LED 灯 */
    }
    return 0;
 }

/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int led_release(struct inode *inode, struct file *filp)
{
    return 0;
}

 /* 设备操作函数 */
static struct file_operations led_fops = {
                                            .owner = THIS_MODULE,
                                            .open = led_open,
                                            .read = led_read,
                                            .write = led_write,
                                            .release = led_release,
                                         };

/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static int __init led_init(void)
{
    int retvalue = 0;
    u32 val = 0;
    /* 初始化 LED */
    /* 1、寄存器地址映射 */
    led_remap();
    /* 2、设置 GPIO1_A3 为 GPIO 功能。*/
    val = readl(BUS_IOC_GPIO1A_IOMUX_SEL_L_PI);
    val &= ~(0XF000 << 0); /* bit15:12，清零 */
    val |= ((0XF000 << 16) | (0X0 << 0)); /* bit31:28 置 1，允许写 bit15:12，bit15:12：0，用作 GPIO1_A3 */
    writel(val, BUS_IOC_GPIO1A_IOMUX_SEL_L_PI);
    /* 3、设置 GPIO0_C0 驱动能力为 40ohm */
    val = readl(VCCIO1_4_IOC_GPIO1A_DS_L_PI);
    val &= ~(0X7000 << 0); /* bit14:12 清零*/
    val |= ((0X7000 << 16) | (0X6000 << 0)); /* bit30:28 置 1，允许写 bit14:12，192 bit14:12：110， 用作 GPIO1_A3 */
    writel(val, VCCIO1_4_IOC_GPIO1A_DS_L_PI);
    /* 4、设置 GPIO1_A3 为输出 */
    val = readl(GPIO_SWPORT_DDR_L_PI);
    val &= ~(0X8 << 0); /* bit3 清零*/
    val |= ((0X8 << 16) | (0X8 << 0)); /* bit19 置 1，允许写 bit3，bit3，高电平 */
    writel(val, GPIO_SWPORT_DDR_L_PI);

    /* 5、设置 GPIO1_A3 为低电平，关闭 LED 灯。*/
    val = readl(GPIO_SWPORT_DR_L_PI);
    val &= ~(0X8 << 0); /* bit3 清零*/
    val |= (0X8 << 16); /* bit19 置 1，允许写 bit3，
    bit3，低电平 */
    writel(val, GPIO_SWPORT_DR_L_PI);

    /* 6、注册字符设备驱动 */
    retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
    if(retvalue < 0) 
    {
        printk(KERN_INFO "register chrdev failed!\r\n");
        goto fail_map;
    }
    return 0;
fail_map:
    led_unmap();
    return -EIO;
}

/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit led_exit(void)
{
    /* 取消映射 */
    led_unmap();
    /* 注销字符设备驱动 */
    unregister_chrdev(LED_MAJOR, LED_NAME);
}




module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JACK");
MODULE_INFO(intree, "Y");
